/*
 * Copyright (c) 2012, Marcin Czekaj
 * All rights reserved.
 *
 * This software is open-source under the BSD license; see LICENSE.TXT for a description.
 */
package pl.edu.uj.fais.binding;

import static javax.swing.KeyStroke.getKeyStroke;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;

import pl.edu.uj.fais.adapters.MouseKeyAdapter;
import pl.edu.uj.fais.controller.Controller;
import pl.edu.uj.fais.mode.GraphMode;
import pl.edu.uj.fais.mode.MouseMode;

public class KeyBinding extends MouseKeyAdapter {
    
    private static KeyBinding instance;
    
    private Controller controller;
    
    private ActionMap actionMap;
    private InputMap inputMap;
    
    private boolean keyDown;
    
    public static KeyBinding getInstance() {
        
        if (instance == null) {
            instance = new KeyBinding();
        }
        
        return instance;
    }
    
    private KeyBinding() {
    }
    
    public void unbind() {
        
        if (inputMap != null) {
            inputMap.clear();
        }
        
        if (actionMap != null) {
            actionMap.clear();
        }
    }
    
    public void rebind(JComponent component) {
        
        unbind();
        bind(component);
        
        component.grabFocus();
    }
    
    private void bind(JComponent component) {
        
        controller = Controller.getInstance();
        
        component.addKeyListener(this);
        component.addMouseListener(this);
        
        inputMap = component.getInputMap(JComponent.WHEN_FOCUSED);
        actionMap = component.getActionMap();
        
        if (controller.getGraphMode() == GraphMode.HYPERGRAPH) {
            bindSelectLinksOnly();
        }
        
        bindSelectAll();
        bindDelete();
        bindToggleAddMode();
        bindToggleTranslateMode();
        bindEditLabel();
    }
    
    @Override
    public void keyReleased(KeyEvent event) {
        
        keyDown = false;
        
        if (event.getModifiers() == 0) {
            controller.setMouseMode(MouseMode.EDIT);
        } else {
            controller.setMouseMode(MouseMode.NONE);
        }
    }
    
    @Override
    public void mouseReleased(MouseEvent event) {
        if (controller.getMouseMode() != MouseMode.LINKS) {
            if (event.getModifiersEx() == 0) {
                controller.setMouseMode(MouseMode.EDIT);
            }
        }
    }
    
    private void bindSelectLinksOnly() {
        bind(getKeyStroke("SPACE"), "select_links_only");
    }
    
    private void bindSelectAll() {
        bind(getKeyStroke("control A"), "select_all");
    }
    
    private void bindDelete() {
        bind(getKeyStroke("DELETE"), "delete_selected");
    }
    
    private void bindToggleAddMode() {
        bind(getKeyStroke("control CONTROL"), "toggle_mode_add");
    }
    
    private void bindToggleTranslateMode() {
        bind(getKeyStroke("shift SHIFT"), "toggle_mode_translate");
    }
    
    private void bindEditLabel() {
        bind(getKeyStroke("F2"), "edit_label");
    }
    
    private void bind(KeyStroke key, String id) {
        inputMap.put(key, id);
        actionMap.put(id, new KeyAction(id));
    }
    
    private class KeyAction extends AbstractAction {
        
        private static final long serialVersionUID = 7475593355926807554L;
        
        private String id;
        
        public KeyAction(String id) {
            this.id = id;
        }
        
        @Override
        public void actionPerformed(ActionEvent event) {
            if (!keyDown) {
                switch (id) {
                    case "select_links_only":
                        keyDown = true;
                        controller.setMouseMode(MouseMode.LINKS);
                        break;
                    case "select_all":
                        keyDown = true;
                        controller.selectAll();
                        break;
                    case "delete_selected":
                        keyDown = true;
                        controller.deleteSelected();
                        break;
                    case "toggle_mode_add":
                        if (controller.getMouseMode() != MouseMode.ADD) {
                            controller.setMouseMode(MouseMode.ADD);
                        }
                        break;
                    case "toggle_mode_translate":
                        keyDown = true;
                        controller.setMouseMode(MouseMode.TRANSLATE);
                        break;
                    case "edit_label":
                        keyDown = true;
                        controller.editLabel();
                        break;
                }
            }
        }
    }
}
